package com.lkd.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.TaskDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.TaskDetailsEntity;
import com.lkd.entity.TaskEntity;
import com.lkd.entity.TaskStatusTypeEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.http.vo.CancelTaskViewModel;
import com.lkd.http.vo.TaskDetailsViewModel;
import com.lkd.http.vo.TaskViewModel;
import com.lkd.service.TaskDetailsService;
import com.lkd.service.TaskService;
import com.lkd.service.TaskStatusTypeService;
import com.lkd.vo.Pager;
import com.lkd.vo.UserVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.configuration.beanutils.BeanHelper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao,TaskEntity> implements TaskService{

    @Autowired
    private TaskStatusTypeService statusTypeService;
    @Autowired
    private VMService vmService;
    @Autowired
    private UserService userService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TaskDetailsService taskDetailsService;
    @Autowired
    private MqttProducer mqttProducer;


    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode, Integer status, Boolean isRepair, String start, String end) {
        Page<TaskEntity> page = new Page<>(pageIndex,pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if(!Strings.isNullOrEmpty(innerCode)){
            qw.eq(TaskEntity::getInnerCode,innerCode);
        }
        if(userId != null && userId > 0){
            qw.eq(TaskEntity::getUserId,userId);
        }
        if(!Strings.isNullOrEmpty(taskCode)){
            qw.like(TaskEntity::getTaskCode,taskCode);
        }
        if(status != null && status > 0){
            qw.eq(TaskEntity::getTaskStatus,status);
        }
        if(isRepair != null){
            if(isRepair){
                qw.ne(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            }else {
                qw.eq(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if(!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)){
            qw
                    .ge(TaskEntity::getCreateTime, LocalDate.parse(start, DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime,LocalDate.parse(end,DateTimeFormatter.ISO_LOCAL_DATE));
        }
        //根据最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getUpdateTime);

        return Pager.build(this.page(page,qw));
    }



    @Override
    public List<TaskStatusTypeEntity> getAllStatus() {
        QueryWrapper<TaskStatusTypeEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .ge(TaskStatusTypeEntity::getStatusId,VMSystem.TASK_STATUS_CREATE);

        return statusTypeService.list(qw);
    }



    /**
     * 创建工单
     *
     * @param taskViewModel
     * @return
     */
    @Override
    @Transactional
    public Boolean createTask(TaskViewModel taskViewModel) {
        TaskEntity taskEntity = new TaskEntity();
        //1.判断用户是否存在
        if (taskViewModel.getAssignorId()==null){
            throw new LogicException("请登录后操作");
        }
        UserVO user = userService.getUser(taskViewModel.getUserId());
//        if (!"1001".equals(user.getRoleCode())){
//            throw new LogicException("无权创建工单");
//        }
        taskEntity.setAssignorId(taskViewModel.getAssignorId());
        //2.判断售货机状态
        VmVO vmInfo = vmService.getVMInfo(taskViewModel.getInnerCode());
        if(vmInfo==null){
            throw new LogicException("售卖机不存在");
        }
        //校验售卖机状态
        checkCreateTask(taskViewModel.getProductType(),vmInfo);
        taskEntity.setInnerCode(taskViewModel.getInnerCode());
        //校验是否存在未完成工单
        if(hasTask(taskViewModel.getInnerCode(),taskViewModel.getProductType())) {
            throw new LogicException("该机器有未完成的同类型工单");
        }
        //3.判断执行人员是否存在
        if (taskViewModel.getUserId()==null){
            throw new LogicException("执行人员不存在");
        }

        taskEntity.setUserId(taskViewModel.getUserId());//执行人id
        taskEntity.setUserName(user.getUserName());//执行人名称
        //补全工单信息
        taskEntity.setTaskCode(generateTaskCode());
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CREATE);//待处理
        taskEntity.setCreateType(1);//工单状态：手动
        taskEntity.setRegionId(vmInfo.getRegionId());//所属区域id
        taskEntity.setProductTypeId(taskViewModel.getProductType());//工单类型id
        //添加运维工单
        save(taskEntity);
        //redis中的工单数量加1
        updateTaskZset(taskEntity,1);

        //判断是否为补货工单
        if (taskViewModel.getProductType()==VMSystem.TASK_TYPE_SUPPLY){
            //补货工单，添加补货信息
            List<TaskDetailsViewModel> details = taskViewModel.getDetails();
            for (TaskDetailsViewModel detail : details) {
                TaskDetailsEntity taskDetailsEntity = new TaskDetailsEntity();
                BeanUtils.copyProperties(detail,taskDetailsEntity);
                taskDetailsEntity.setTaskId(taskEntity.getTaskId());
                taskDetailsService.save(taskDetailsEntity);
            }
        }
        return true;
    }

    /**
     * 取消工单
     *
     * @param taskId
     * @param cancelTaskViewModel
     * @param userId
     */
    @Override
    public Boolean cancel(String taskId, CancelTaskViewModel cancelTaskViewModel, Integer userId) {

        Long id = Long.valueOf(taskId);
        TaskEntity taskEntity = getById(id);
        //1.判断登录用户和操作用户是否为统一用户
        if (taskEntity==null){
            throw new LogicException("工单不存在");
        }
        if (taskEntity.getUserId().intValue() !=userId.intValue()){
            throw new LogicException("非法操作");
        }
        //2.判断工单状态是否已结束
        if(taskEntity.getTaskStatus()==VMSystem.TASK_STATUS_CANCEL||taskEntity.getTaskStatus()==VMSystem.TASK_TYPE_REVOKE){
            throw new LogicException("工单已完成，不可再撤销");
        }
        //3.修改工单状态为：取消
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CANCEL);
        taskEntity.setDesc(cancelTaskViewModel.getDesc());
        updateById(taskEntity);

        //取消工单： redis中的工单数量-1
        updateTaskZset(taskEntity,-1);
        return true;
    }

    /**
     * 接受工单
     *
     * @param taskId
     * @return
     */
    @Override
    public Boolean acceptTask(String taskId,Integer userId) {
        Long id = Long.valueOf(taskId);
        TaskEntity taskEntity = getById(id);
        //1.判断登录用户和操作用户是否为统一用户
        if (taskEntity==null){
            throw new LogicException("工单不存在");
        }
        if (taskEntity.getUserId().intValue() !=userId.intValue()){
            throw new LogicException("非法操作");
        }
        //2.判断工单状态是否为待处理
        if(taskEntity.getTaskStatus() !=VMSystem.TASK_STATUS_CREATE){
            throw new LogicException("工单已取消或已接受");
        }
        //3.修改工单状态为：进行中
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_PROGRESS);
        return updateById(taskEntity);
    }

    /**
     * 完成工单
     *
     * @param taskId
     * @param userId
     * @return
     */
    @Override
    public Boolean complete(String taskId, Integer userId) {
        Long id = Long.valueOf(taskId);
        TaskEntity taskEntity = getById(id);
        //1.判断登录用户和操作用户是否为统一用户
        if (taskEntity==null){
            throw new LogicException("工单不存在");
        }
        if (taskEntity.getUserId().intValue() !=userId.intValue()){
            throw new LogicException("非法操作");
        }
        //2.判断工单状态是否为取消或已完成
        if(!taskEntity.getTaskStatus().equals(VMSystem.TASK_STATUS_PROGRESS)){
            throw new LogicException("工单不可完成");
        }
        //3.修改工单状态为：进行中
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_FINISH);
        //如果是投放工单或撤机工单
        if (taskEntity.getProductTypeId()==VMSystem.TASK_TYPE_DEPLOY||taskEntity.getProductTypeId()==VMSystem.TASK_TYPE_REVOKE){
            noticeVMServiceStatus(taskEntity);//运维工单封装与下发
        }
        //如果是补货工单
        if (taskEntity.getProductTypeId()==VMSystem.TASK_TYPE_SUPPLY){
            noticeVMServiceSupply(taskEntity);//补货工单下发通知
        }

        return updateById(taskEntity);
    }

    /**
     * 更新redis中的工单数
     * @param taskEntity
     * @param score 创建工单或取消工单
     */
    @Override
    public void updateTaskZset(TaskEntity taskEntity, int score) {
        String roleCode="1003";//运维人员
        if (taskEntity.getProductTypeId().intValue()==2){
        //补货工单，角色设置为运营人员
            roleCode="1002";
        }
        //根据角色分类，修改对应redis中key对应的分值，从而修改对应人员的工单数量
        String key=VMSystem.REGION_TASK_KEY_PREF
                +LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                +"."+taskEntity.getRegionId()
                +"."+roleCode;
        redisTemplate.opsForZSet().incrementScore(key,taskEntity.getUserId(),score);
    }

    /**
     * 获取同一天内分配的工单最少的人
     *
     * @param regionId
     * @param isRepair 是否是运维工单
     * @return 用户id
     */
    @Override
    public Integer getLeastUser(Long regionId, Boolean isRepair) {
        String roleCode="1002";
        if (isRepair){
            roleCode="1003";
        }
        String key=VMSystem.REGION_TASK_KEY_PREF
                +LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                +"."+regionId+"."+roleCode;
        Set<Object> set = redisTemplate.opsForZSet().range(key, 0, 0);
        if (set==null||set.isEmpty()){
            return 0;//如果没有数据返回0；
        }

        return (Integer) set.stream().collect(Collectors.toList()).get(0);
    }

    /**
     * 补货工单通知
     * @param taskEntity
     */
    private void noticeVMServiceSupply(TaskEntity taskEntity) {
        SupplyContract supplyContract = new SupplyContract();//补货封装类
        //根据任务id获取补货工单详情
        List<TaskDetailsEntity> taskDetailsEntities = taskDetailsService.getByTaskId(taskEntity.getTaskId());
        ArrayList<SupplyChannel> supplyChannels = new ArrayList<>();
        supplyContract.setInnerCode(taskEntity.getInnerCode());
        //补全supplyChannels数据
        for (TaskDetailsEntity taskDetailsEntity : taskDetailsEntities) {
            SupplyChannel supplyChannel = new SupplyChannel();
            supplyChannel.setChannelId(taskDetailsEntity.getChannelCode());
            supplyChannel.setCapacity(taskDetailsEntity.getExpectCapacity());
            supplyChannel.setSkuId(taskDetailsEntity.getSkuId());
            supplyChannel.setSkuName(taskDetailsEntity.getSkuName());
            supplyChannel.setSkuImage(taskDetailsEntity.getSkuImage());
            supplyChannels.add(supplyChannel);
        }
        supplyContract.setSupplyData(supplyChannels);
        try {
            //发送消息
            mqttProducer.send(TopicConfig.VMS_SUPPLY_TOPIC,2,supplyContract);
            log.info("发送消息成功");
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }


    }

    /**
     * 运维工单封装与下发
     * @param taskEntity
     */
    private void noticeVMServiceStatus(TaskEntity taskEntity) {
        try {
            //发送EMQx消息：实现异步通知售货机修改售货机状态
            TaskCompleteContract taskCompleteContract = new TaskCompleteContract();
            taskCompleteContract.setInnerCode(taskEntity.getInnerCode());
            taskCompleteContract.setTaskType(taskEntity.getProductTypeId());
            mqttProducer.send(TopicConfig.VMS_COMPLETED_TOPIC,0,taskCompleteContract);
            log.info("发送消息成功");
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 校验是否存在未完成工单
     * @param innerCode
     * @param productType
     * @return
     */
    private boolean hasTask(String innerCode, int productType) {
        //查询该机器未完成工单情况
        LambdaQueryWrapper<TaskEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(TaskEntity::getInnerCode,innerCode)
                          .eq(TaskEntity::getProductTypeId,productType)
                          .le(TaskEntity::getTaskStatus,VMSystem.TASK_STATUS_PROGRESS);//工单未完成
        //
        return  this.count(lambdaQueryWrapper)>0;
    }

    /**
     * 创建工单校验
     * @param productType
     * @param vmInfo
     */
    private void checkCreateTask(int productType, VmVO vmInfo) {
        //1.投放工单
        if (VMSystem.TASK_TYPE_DEPLOY==productType && vmInfo.getVmStatus()==VMSystem.VM_STATUS_RUNNING){
            throw new LogicException("售卖机已在运营。。。。");
        }
        //2.补货工单
        if (VMSystem.TASK_TYPE_SUPPLY==productType && vmInfo.getVmStatus()!=VMSystem.VM_STATUS_RUNNING){
            throw new LogicException("售卖机不在运营状态。。。");
        }
        //3.维修工单
        if (VMSystem.TASK_TYPE_REPAIR==productType && vmInfo.getVmStatus()!=VMSystem.VM_STATUS_RUNNING){
            throw new LogicException("售卖机不在运营状态");
        }
        //4.撤机工单
        if (VMSystem.TASK_TYPE_REVOKE==productType && vmInfo.getVmStatus()!=VMSystem.VM_STATUS_RUNNING){
            throw new LogicException("售卖机不在运营状态");

        }

    }

    /**
     * 生成工单号
     *
     * @return
     */
    private String generateTaskCode() {
        //日期+序号
        String date =LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String key="lkd.task.code."+date;//redis 的key
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Object obj = valueOperations.get(key);
        if (obj==null){
            redisTemplate.opsForValue().set(key,1L, Duration.ofDays(1));
            return date+"0001";

        }
        return date + Strings.padStart(redisTemplate.opsForValue().increment(key,1).toString(),4,'0');
    }


}
